﻿Imports System.Web
Imports System.Security.Cryptography
Imports System.Text
Imports System.IO
Imports System.Runtime.InteropServices

Namespace Cryptography
    Public Class Crypto
        '  Llamar a esta función para quitar la clave de la memoria después de su uso por seguridad 
        <System.Runtime.InteropServices.DllImport("KERNEL32.DLL", EntryPoint:="RtlZeroMemory")> _
        Public Shared Function ZeroMemory(ByVal Destination As IntPtr, ByVal Length As Integer) As Boolean
        End Function

        ' Función para generar una clave de 64 bits. 
        Private Shared Function GenerateKey() As String
            ' Crear una instancia del algoritmo simétrico. La clave y el IV se generan automáticamente. 
            Dim desCrypto As DESCryptoServiceProvider = DirectCast(DESCryptoServiceProvider.Create(), DESCryptoServiceProvider)

            ' Utilizar la clave generada automáticamente para el cifrado. 
            Return ASCIIEncoding.ASCII.GetString(desCrypto.Key)
        End Function

        Private Shared ReadOnly Property getCryptoKey() As String
            Get
                Return (Configuration.CfgManager.getNodeText(Of String)("cryptokey") & "$CiLiÑk$").Substring(0, 8)
            End Get
        End Property


        
        Private Shared mutex As New Threading.Mutex(False, "TestFile")
        Public Shared Sub EncryptFile(ByVal FileName As String)
            Dim fileOut = Path.GetTempFileName()
            File.Delete(fileOut)
            ' Debe ser 64 bits, 8 bytes. 
            ' Distribuir esta clave al usuario que descifrará este archivo. 
            Dim sSecretKey As String

            ' Obtener la clave para que el archivo se cifre.
            sSecretKey = getCryptoKey()

            ' Para mayor seguridad, fijar la clave. 
            Dim gch As GCHandle = GCHandle.Alloc(sSecretKey, GCHandleType.Pinned)

            ' Cifrar el archivo. 
            EncryptFile(FileName, fileOut, sSecretKey)

            Try
                mutex.WaitOne()
                File.Delete(FileName)
            Catch ex As Exception
                Throw (ex)
            Finally
                mutex.ReleaseMutex()
            End Try


            Try
                mutex.WaitOne()
                File.Copy(fileOut, FileName)
            Catch ex As Exception
                Throw (ex)
            Finally
                mutex.ReleaseMutex()
            End Try

            ' Quitar la clave de la memoria. 
            ZeroMemory(gch.AddrOfPinnedObject(), sSecretKey.Length * 2)
            gch.Free()
        End Sub

        Public Shared Sub DecryptFile(ByVal FileName As String)
            Dim fileOut = Path.GetTempFileName()
            File.Delete(fileOut)
            ' Debe ser 64 bits, 8 bytes. 
            ' Distribuir esta clave al usuario que descifrará este archivo. 
            Dim sSecretKey As String

            ' Obtener la clave para que el archivo se cifre.
            sSecretKey = getCryptoKey()

            ' Para mayor seguridad, fijar la clave. 
            Dim gch As GCHandle = GCHandle.Alloc(sSecretKey, GCHandleType.Pinned)

            ' Cifrar el archivo. 
            DecryptFile(FileName, fileOut, sSecretKey)

            mutex.WaitOne()
            File.Delete(FileName)
            mutex.ReleaseMutex()
            mutex.WaitOne()
            File.Copy(fileOut, FileName)
            mutex.ReleaseMutex()



            ' Quitar la clave de la memoria. 
            ZeroMemory(gch.AddrOfPinnedObject(), sSecretKey.Length * 2)
            gch.Free()
        End Sub



        Private Shared Sub EncryptFile(ByVal sInputFilename As String, ByVal sOutputFilename As String, ByVal sKey As String)
            mutex.WaitOne()
            Try
                Using fsInput As New FileStream(sInputFilename, FileMode.Open, FileAccess.Read)
                    Using fsEncrypted As New FileStream(sOutputFilename, FileMode.Create, FileAccess.Write)
                        Dim DES As New DESCryptoServiceProvider()
                        DES.Key = ASCIIEncoding.ASCII.GetBytes(sKey)
                        DES.IV = ASCIIEncoding.ASCII.GetBytes(sKey)
                        Dim desencrypt As ICryptoTransform = DES.CreateEncryptor()

                        Using cryptostream As New CryptoStream(fsEncrypted, desencrypt, CryptoStreamMode.Write)
                            Dim bytearrayinput As Byte() = New Byte(CInt(fsInput.Length - 1)) {}
                            fsInput.Read(bytearrayinput, 0, bytearrayinput.Length)
                            cryptostream.Write(bytearrayinput, 0, bytearrayinput.Length)
                            cryptostream.Close()

                        End Using
                        fsEncrypted.Close()
                    End Using
                    fsInput.Close()
                End Using
            Catch ex As Exception
                Throw (ex)
            Finally
                mutex.ReleaseMutex()
            End Try

        End Sub

        Private Shared Sub DecryptFile(ByVal sInputFilename As String, ByVal sOutputFilename As String, ByVal sKey As String)
            mutex.WaitOne()
            Try
                Dim DES As New DESCryptoServiceProvider()
                'Se necesita una clave de 64 bits y un IV para este proveedor. 
                'Establecer la clave secreta para el algoritmo DES. 
                DES.Key = ASCIIEncoding.ASCII.GetBytes(sKey)
                'Establecer el vector de inicialización. 
                DES.IV = ASCIIEncoding.ASCII.GetBytes(sKey)
                'Crear una secuencia de archivo para volver a leer el archivo cifrado. 
                Using fsread As New FileStream(sInputFilename, FileMode.Open, FileAccess.Read)
                    'Crear un descriptor de DES desde la instancia de DES. 
                    Dim desdecrypt As ICryptoTransform = DES.CreateDecryptor()
                    'Crear una secuencia de cifrado para leer y 
                    'realizar una transformación de cifrado DES en los bytes de entrada. 
                    Using cryptostreamDecr As New CryptoStream(fsread, desdecrypt, CryptoStreamMode.Read)
                        'Imprimir el contenido del archivo descifrado. 
                        Using fsDecrypted As New StreamWriter(sOutputFilename)
                            fsDecrypted.Write(New StreamReader(cryptostreamDecr).ReadToEnd())
                            fsDecrypted.Flush()
                            fsDecrypted.Close()
                        End Using
                    End Using
                End Using

            Catch ex As Exception
                Throw (ex)
            Finally
                mutex.ReleaseMutex()
            End Try
        End Sub


        Public Shared Function Encrypt(ByVal value As String) As String
            'value = value & getCryptoKey
            'Dim byt As Byte() = System.Text.Encoding.UTF8.GetBytes(value)
            'Return Convert.ToBase64String(byt)
            If String.IsNullOrEmpty(value) Then
                Return String.Empty
            End If
            Dim e As New Encryption64()
            value = e.Encrypt(value, getCryptoKey())
            Return value

        End Function

        Public Shared Function Encrypt(ByVal value As String, ByVal CryptoKey As String) As String
            'value = value & getCryptoKey
            'Dim byt As Byte() = System.Text.Encoding.UTF8.GetBytes(value)
            'Return Convert.ToBase64String(byt)
            If String.IsNullOrEmpty(value) Then
                Return String.Empty
            End If
            Dim e As New Encryption64()
            value = e.Encrypt(value, CryptoKey)
            Return value

        End Function
        Public Shared Function Decrypt(ByVal value As String) As String
            'Dim b As Byte() = Convert.FromBase64String(value)
            'Dim ck = System.Text.Encoding.UTF8.GetString(b)
            'Return ck.Remove(ck.Length - getCryptoKey.Length)
            If String.IsNullOrEmpty(value) Then
                Return String.Empty
            End If
            Dim e As New Encryption64()
            Return e.Decrypt(value, getCryptoKey())

        End Function


        Public Shared Function Decrypt(ByVal value As String, ByVal CryptoKey As String) As String
            'Dim b As Byte() = Convert.FromBase64String(value)
            'Dim ck = System.Text.Encoding.UTF8.GetString(b)
            'Return ck.Remove(ck.Length - getCryptoKey.Length)
            If String.IsNullOrEmpty(value) Then
                Return String.Empty
            End If
            Dim e As New Encryption64()
            Return e.Decrypt(value, CryptoKey)

        End Function


    End Class

End Namespace